package com.google.code.peersim.pastry.protocol;

import com.google.code.peersim.metrics.EuclideanMetric.EuclideanMetricMeasurableIfc;
import com.google.code.peersim.metrics.ProximityMetricIfc;
import java.util.Random;

/**
 * {@link PastryNodeDescriptor}s are used to store a node's meta-data to allow
 * {@link PastryProtocol} internal data-structures to store information about
 * known nodes.<br>
 * A {@link PastryNodeDescriptor} mainly holds the following information:
 * <ol>
 * <li>the {@link PastryId} of the node it refers to</li>
 * <li>a proper representation of the position the node has in the network</li>
 * </ol>
 * The position information above is used to compute distances between nodes.
 * For this reason a {@link PastryNodeDescriptor} has to implement all those
 * {@link ProximityMetricIfc} sub-interfaces the simulation might require.
 *
 * @see ProximityMetricIfc
 * @see EuclideanMetricMeasurableIfc
 *
 * @author frusso
 * @version 0.1
 * @since 0.1
 */
final class PastryNodeDescriptor implements EuclideanMetricMeasurableIfc {

  private final PastryId pastryId;
  private final Random r;
  private final int x, y;
  
  /**
   * Static factory method for {@link PastryNodeDescriptor} instantiation based on the provided
   * {@link PastryId} and {@link ProximityMetricIfc.MeasurableEntityIfc}.
   * Since {@link PastryNodeDescriptor} instances are immutable, this method is
   * allowed to return the very same {@link PastryNodeDescriptor} instance when supplied with
   * an already seen {@link PastryId} instance (remember that also {@link PastryId}s
   * are immutable.
   *
   * @param id The {@link PastryId} instance for which an {@link Entry} instance is requested
   * @param otherCoordinates Holds the location information of the node
   * @return The required {@link Entry} instance
   */
  static final PastryNodeDescriptor instanceFor(PastryId id, ProximityMetricIfc.MeasurableEntityIfc otherCoordinates) {
    return new PastryNodeDescriptor(id, otherCoordinates, null);
  }

  /**
   * Static factory method used to instantiate {@link PastryNodeDescriptor}s only
   * for testing purposes.
   *
   * @param id The {@link PastryId}
   * @param r The source of randomness
   * @return The new {@link PastryNodeDescriptor}
   */
  static final PastryNodeDescriptor forTesting(PastryId id, Random r) {
    return new PastryNodeDescriptor(id, null, r);
  }

  /**
   * Private constructor used by static factory methods.
   *
   * @param id The {@link PastryId}
   * @param otherCoordinates Holds the location information of the node
   * @param r The source of randomness
   */
  private PastryNodeDescriptor(PastryId id, ProximityMetricIfc.MeasurableEntityIfc otherCoordinates, Random r) {
    pastryId = id;
    if(r!=null) {
      this.r = r;
      x = r.nextInt();
      y = r.nextInt();
    } else {
      this.r = null;
      x = ((EuclideanMetricMeasurableIfc)otherCoordinates).getX();
      y = ((EuclideanMetricMeasurableIfc)otherCoordinates).getY();
    }
  }

  /**
   * Two {@link PastryNodeDescriptor}s equal iff they refer to the very same
   * {@link PastryId}.
   * 
   * @see PastryId#equals(java.lang.Object)
   *
   * @param other
   * @return
   */
  @Override
  public final boolean equals(Object other) {
    if(this==other)
      return true;
    if(!(other instanceof PastryNodeDescriptor))
      return false;
    PastryNodeDescriptor otherPND = (PastryNodeDescriptor)other;
    return pastryId.equals(otherPND.pastryId);
  }

  /**
   * The {@link PastryId} stored in this routingtable entry.
   *
   * @return The {@link PastryId}
   */
  public final PastryId getPastryId() {
    return pastryId;
  }

  /**
   * The reference to the {@link PastryNode} described by this instance.
   * @return The reference to the {@link PastryNode} described by this instance.
   */
  public final PastryNode getPastryNode() {
    return pastryId.getNode();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final int getX() {
    return x;
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final int getY() {
    return y;
  }

  /**
   * Computes a {@link PastryNodeDescriptor}'s hash-code as the associated
   * {@link PastryId} hash-code.
   *
   * @return The hash-code
   */
  @Override
  public final int hashCode() {
    return pastryId.hashCode();
  }

  /**
   * {@inheritDoc}
   */
  @Override
  public final String toString() {
    return pastryId.toString()+" ["+x+","+y+"]";
  }
}